<?php
namespace app\api\logic;

use app\api\cache\KeysUtil;
use app\api\cache\MemberCache;
use app\api\cache\RedisCache;
use app\api\consDir\ErrorConst;
use app\api\services\MemberService;
use app\api\services\RabbitMqService;
use app\api\services\WechatPayService;
use app\common\libs\Jwt;
use app\common\libs\Singleton;
use app\common\models\Member\Member;
use app\common\models\Member\MemberInfo;
use app\common\models\Service\Service;
use app\common\utils\CommonUtil;
use app\common\utils\RedLock;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;

/**
 * 登录模块
 * Class MemberLogic
 * @package app\api\logic
 */
class LoginLogic extends BaseLogic
{
    use Singleton;

    public function bindAddress($inviteCode, $provinceId, $cityId, $countryId, $streetId, $commId): bool
    {
        $data = [
            'province_id' => $provinceId,
            'city_id'     => $cityId,
            'country_id'  => $countryId,
            'street_id'   => $streetId,
            'comm_id'     => $commId,
        ];
        if ($inviteCode > 0) {
            $this->getUserInviteInfo($data, $inviteCode);
        }

        Member::getInstance()->where('id', $this->userinfo->id)->update($data);
        MemberCache::delUserInfo($this->userinfo->id);
        return true;
    }

    public function phoneLogin($phone, $code, $type, $password, $prefix = ''): array
    {
        if ( ! in_array($type, [1, 2])) {
            CommonUtil::throwException(ErrorConst::PARAM_ERROR, ErrorConst::PARAM_ERROR_MSG);
        }

        //短信登录
        if ($type == 1) {
            $getCode = MemberCache::getUserCode($phone, 'Login');
            if (empty($getCode) || $getCode != $code) {
                CommonUtil::throwException(ErrorConst::CODE_ERROR, ErrorConst::CODE_ERROR_MSG);
            }
        }

        $userInfo = MemberService::getInstance()->getUserInfoByPhone($phone);
        if (empty($userInfo)) {
            CommonUtil::throwException(ErrorConst::LOGIN_ERROR, ErrorConst::LOGIN_ERROR_MSG);
        }
        if ($prefix == 'agency' && $userInfo['isAgency'] == 0) {
            CommonUtil::throwException(ErrorConst::LOGIN_ERROR, '账号错误');
        }

        if ($userInfo['isLogout']){
            CommonUtil::throwException(ErrorConst::LOGIN_ERROR, '账号已注销');
        }

        if ($userInfo['status'] == 0){
            CommonUtil::throwException(ErrorConst::LOGIN_ERROR, '账号已禁用');
        }

        // var_dump($userInfo['isAgency'] == 0);exit;
        //密码登录
        if ($type == 2) {
            if (md5($password) != $userInfo['password']) {
                CommonUtil::throwException(ErrorConst::ACCOUNT_PASSWORD_ERROR, ErrorConst::ACCOUNT_PASSWORD_ERROR_MSG);
            }
        }
        $data = [
            'username'  => $userInfo['userName'],
            'id'        => $userInfo['id'],
            'phone'     => $userInfo['phone'],
            //'openid' => $userInfo['openId'],
            'role'      => $prefix,
            'loginTime' => time(),
        ];
        $event["exchange"] = config('rabbitmq.info_queue');
        RabbitMqService::send($event, ['type' => 'member', 'data' => ['id' => $userInfo['id']]]);

        $token = $this->setToken($userInfo['id'], $data, $prefix);
        return ['token' => $token];
    }

    /**
     * @param $phone
     * @param $code
     * @param $inviteCode
     * @param $provinceId
     * @param $cityId
     * @param $countryId
     * @param $streetId
     * @param $commId
     * @param $openId
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function reg($phone, $code, $inviteCode, $provinceId, $cityId, $countryId, $streetId, $commId, $openId, $password, $rePassword)
    {
        if ( ! RedLock::getInstance()->lock('reg:' . $this->userinfo->id, 1)) {
            CommonUtil::throwException(ErrorConst::FREQUENT_ERROR, ErrorConst::FREQUENT_ERROR_MSG);
        }

        if (empty($commId)) {
            CommonUtil::throwException(ErrorConst::PARAM_ERROR, ErrorConst::PARAM_ERROR_MSG);
        }

        if ($password != $rePassword) {
            CommonUtil::throwException(ErrorConst::RE_PASSWORD_ERROR, ErrorConst::RE_PASSWORD_ERROR_MSG);
        }

        $getCode = MemberCache::getUserCode($phone, 'Reg');
        if (empty($getCode) || $getCode != $code) {
            CommonUtil::throwException(ErrorConst::CODE_ERROR, ErrorConst::CODE_ERROR_MSG);
        }

        $userInfo = MemberService::getInstance()->getUserInfoByPhone($phone);
        if(!empty($userInfo) && $userInfo['isLogout'] == 1){
            CommonUtil::throwException(ErrorConst::PHONE_ERROR, '账号已注销，不能重新注册');
        }
        if ( ! empty($userInfo)) {
            CommonUtil::throwException(ErrorConst::PHONE_ERROR, ErrorConst::PHONE_ERROR_MSG);
        }

        $where = [
            ['phone','=',$phone.'_'],
            ['is_logout','=','1'],
            ['invite_id','>',0]
        ];
        $count = Member::getInstance()->where($where)->count();
        if($count > 0){
            CommonUtil::throwException(ErrorConst::PHONE_ERROR, '账号已注销，不能重新注册');
        }

        $data = [
            'phone'       => $phone,
            'user_name'   => substr_replace($phone, '****', 3, 4),
            //'open_id' => $openId,
            'avatar'      => '',
            'province_id' => $provinceId,
            'city_id'     => $cityId,
            'country_id'  => $countryId,
            'street_id'   => $streetId,
            'comm_id'     => $commId,
            'password'    => md5($password),
        ];
        if ( ! empty($inviteCode)) {
            $this->getUserInviteInfo($data, $inviteCode);
        }
        if(!empty($data['invite_id'])){
            $community_order_id = RedisCache::get(KeysUtil::getCommunityTaskKey($data['invite_id']));
            if(!empty($community_order_id)){
                $data['community_order_id'] = $community_order_id;
            }
        }
        Member::getInstance()->startTrans();
        try {
            $id = Member::getInstance()->insertGetId($data);
            Member::getInstance()->where('id', $id)->update(['invite_code' => CommonUtil::getInviteCode($id)]);
        } catch (\Exception $e) {
            Member::getInstance()->rollback();
            CommonUtil::throwException(ErrorConst::REG_ERROR, $e->getMessage());
        }
        Member::getInstance()->commit();

        $return = [
            'username'  => $data['user_name'],
            'id'        => $id,
            'phone'     => $phone,
            //'openId' => $openId,
            'loginTime' => time(),
        ];
        $event["exchange"] = config('rabbitmq.info_queue');
        RabbitMqService::send($event, ['type' => 'member', 'data' => ['id' => $id]]);
        RabbitMqService::send($event, ['type' => 'gxz', 'data' => ['id' => $id]]);
        if(!empty($data['invite_id'])){
            RabbitMqService::send($event, ['type' => 'lottery_add', 'data' => ['id' => $data['invite_id']]]);
        }
        $token = $this->setToken($id, $return);
        return ['token' => $token];
    }

    public function setToken($userId, $data, $prefix = '')
    {
        $token = Jwt::getToken($data);
        \app\common\utils\RedisManager::conn([
            'persistent' => false,
            'select'     => 1,
        ])->setEx(
            KeysUtil::getMemberInfoTokenKey($userId, $prefix),
            RedisCache::ONE_MONTH, // 86400 * 30
            $token);
        return $token;
    }

    /**
     * @param $data
     * @param $inviteCode
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getUserInviteInfo(&$data, $inviteCode)
    {
        //$inviteId = MemberService::getInstance()->decode($inviteCode);
        $inviteId   = Member::getInstance()->where('invite_code', $inviteCode)->value('id');
        $inviteUser = MemberService::getInstance()->getUserInfo($inviteId);
        if ( ! empty($inviteUser)) {
            $data['invite_id']       = $inviteId;
            $data['invite_third_id'] = $inviteUser['inviteId'];
            $data['invite_ids']      = $inviteUser['inviteIds'] . $inviteId . '-';
        }
    }

    /**
     * @param $code
     * @param $iv
     * @param $encryptedData
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function bindPhone($code, $iv, $encryptedData): bool
    {
        if ( ! RedLock::getInstance()->lock('bindPhone_' . $this->userinfo->id, 1)) {
            CommonUtil::throwException(ErrorConst::FREQUENT_ERROR, ErrorConst::FREQUENT_ERROR_MSG);
        }
        $codeInfo = $this->code($code);
        if ( ! $codeInfo['isReg']) {
            CommonUtil::throwException(ErrorConst::LOGIN_ERROR, ErrorConst::LOGIN_ERROR_MSG);
        }
        $userInfo = $this->appletLogin($iv, $codeInfo['session_key'], $encryptedData);

        MemberService::getInstance()->editVal($codeInfo['id'], ['phone' => $userInfo['phoneNumber']]);

        MemberInfo::getInstance()->insert(['text' => json_encode($userInfo)]);
        return true;
    }

    /**
     * @param $code
     * @param $iv
     * @param $encryptedData
     * @param $inviteCode
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function codeLogin($code, $iv, $encryptedData, $inviteCode): array
    {
        if ( ! RedLock::getInstance()->lock('codeLogin_' . $this->userinfo->id, 1)) {
            CommonUtil::throwException(ErrorConst::FREQUENT_ERROR, ErrorConst::FREQUENT_ERROR_MSG);
        }
        $codeInfo = $this->code($code);
        if ($codeInfo['isReg']) {
            return ['token' => $codeInfo['token']];
        }
        $userInfo = $this->appletLogin($iv, $codeInfo['session_key'], $encryptedData);

        $data = [
            'user_name' => $userInfo['nickName'],
            'open_id'   => $codeInfo['openid'],
            'avatar'    => $userInfo['avatarUrl'],
        ];

        if ($inviteCode > 0) {
            $this->getUserInviteInfo($data, $inviteCode);
        }
        $id = Member::getInstance()->insertGetId($data);

        $data = [
            'username'  => $userInfo['nickName'],
            'id'        => $id,
            'phone'     => '',
            'openId'    => $codeInfo['openid'],
            'loginTime' => time(),
        ];
        return ['token' => Jwt::getToken($data)];
    }

    /**
     * @param $iv
     * @param $sessionKey
     * @param $encryptedData
     * @return bool
     */
    public function appletLogin($iv, $sessionKey, $encryptedData)
    {
        $data = WechatPayService::getInstance()->appletLogin($iv, $sessionKey, $encryptedData);
        if ( ! $data) {
            CommonUtil::throwException(ErrorConst::PARAM_ERROR, ErrorConst::PARAM_ERROR_MSG);
        }
        MemberInfo::getInstance()->insert(['text' => json_encode($data)]);
        return $data;
    }

    /**
     * @param $code
     * @return array|false
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function code($code)
    {
        $data = WechatPayService::getInstance()->code($code);
        if ( ! $data || ! isset($data['openid'])) {
            CommonUtil::throwException(ErrorConst::WECHAT_CODE_ERROR, ErrorConst::WECHAT_CODE_ERROR_MSG);
        }
        $userInfo = MemberService::getInstance()->getUserInfoByOpenid($data['openid']);

        $data['isReg'] = 0;
        if ( ! empty($userInfo)) {
            $data = [
                'username'    => $userInfo['userName'],
                'id'          => $userInfo['userId'],
                'phone'       => $userInfo['phone'],
                'openid'      => $data['openid'],
                'loginTime'   => time(),
                'session_key' => $data['session_key'],
            ];
            return ['isReg' => 1, 'id' => $data['id'], 'token' => Jwt::getToken($data), 'openid' => $data['openid'], 'session_key' => $data['session_key']];
        }
        return $data;
    }

    /**
     * @throws ModelNotFoundException
     * @throws DataNotFoundException
     * @throws DbException
     */
    public function resetPwd($phone, $code, $password): bool
    {
        if (empty($phone) || empty($password)) {
            CommonUtil::throwException(ErrorConst::PARAM_ERROR, ErrorConst::PARAM_ERROR_MSG);
        }
        if ( ! preg_match("/^1\d{10}$/", $phone)) {
            CommonUtil::throwException(ErrorConst::PHONE_WRITE_ERROR, ErrorConst::PHONE_WRITE_ERROR_MSG);
        }
        $getCode = MemberCache::getUserCode($phone, 'Reset');
        if (empty($getCode) || $getCode != $code) {
            CommonUtil::throwException(ErrorConst::CODE_ERROR, ErrorConst::CODE_ERROR_MSG);
        }
        $userInfo = MemberService::getInstance()->getUserInfoByPhone($phone);
        if (empty($userInfo)) {
            CommonUtil::throwException(ErrorConst::PHONE_NOT_EXIST, ErrorConst::PHONE_NOT_EXIST_MSG);
        }
        MemberService::getInstance()->editVal($userInfo['id'], ['password' => md5($password)]);
        return true;
    }

    /**
     * @throws ModelNotFoundException
     * @throws DataNotFoundException
     * @throws DbException
     */
    public function loftLogin($phone, $password): array
    {
        $member = Member::getInstance()->where('phone',$phone)->find();
        if(empty($member)){
            CommonUtil::throwException(ErrorConst::ACCOUNT_PASSWORD_ERROR, '账号不存在');
        }
        if (md5($password) != $member['password']) {
            CommonUtil::throwException(ErrorConst::ACCOUNT_PASSWORD_ERROR, ErrorConst::ACCOUNT_PASSWORD_ERROR_MSG);
        }
        $service = Service::getInstance()->where('user_id',$member['id'])->find();
        if(empty($service)){
            CommonUtil::throwException(ErrorConst::ACCOUNT_PASSWORD_ERROR, '不是生活馆账号');
        }
        $data = [
            'username'  => $member['user_name'],
            'id'        => $member['id'],
            'phone'     => $phone,
            //'openId' => $openId,
            'loginTime' => time(),
        ];
        $token = $this->setToken($member['id'], $data, '');
        return ['token' => $token];
    }
}
